#include "Roster.h"

#include "RosterGroup.h"
#include "PacketCollector.h"
#include "XMPPConnection.h"
#include "SmackConfiguration.h"
#include "RosterEntry.h"
#include "RosterListener.h"

#include <filter/PacketTypeFilter.h>
#include <filter/PacketIDFilter.h>

#include <packet/Presence.h>
#include <packet/RosterPacket.h>

#include <util/StringUtils.h>



Roster::SubscriptionMode Roster::defaultSubscriptionMode = ACCEPT_ALL;

Roster::SubscriptionMode Roster::getDefaultSubscriptionMode() {
	return defaultSubscriptionMode;
}


void Roster::setDefaultSubscriptionMode(SubscriptionMode subscriptionMode) {
        defaultSubscriptionMode = subscriptionMode;
}


Roster::Roster( XMPPConnection * connection) :
	connection(connection)
{
	// Listen for any roster packets.
	PacketFilter * rosterFilter = new PacketTypeFilter(typeid(RosterPacket));

	connection->addPacketListener(new RosterPacketListener(), rosterFilter);

	// Listen for any presence packets.
    PacketFilter * presenceFilter = new PacketTypeFilter(typeid(Presence));
	presencePacketListener = new PresencePacketListener();
    connection->addPacketListener(presencePacketListener, presenceFilter);
	connection->addConnectionListener(new OfflineConnectionListener(this));
}


void Roster::reload() {
    connection->sendPacket(new RosterPacket());
}


void Roster::addRosterListener(RosterListener * rosterListener) {
	rosterListeners.insert(rosterListener);
}


void Roster::removeRosterListener(RosterListener * rosterListener) {
	rosterListeners.erase(rosterListener);
}


RosterGroup * Roster::createGroup(std::string & name) {
	if (groups.find(name) != groups.end()) {
		return NULL;
	}
	RosterGroup * group = new RosterGroup(name, connection);
	groups[name] = group;
	return group;
}


void Roster::createEntry(std::string & user, std::string & name, std::vector<std::string> & groups) {
	// Create and send roster entry creation packet.
	RosterPacket * rosterPacket = new RosterPacket();
	rosterPacket->setType(IQ::IQ_TYPE_SET);
	RosterPacket::Item * item = new RosterPacket::Item(user, name);
	if (!groups.empty()) {
		for( std::vector<std::string>::iterator it = groups.begin(); it != groups.end(); it++) {
			item->addGroupName(*it);
		}
	}
	rosterPacket->addRosterItem(item);
	// Wait up to a certain number of seconds for a reply from the server.
	PacketCollector * collector = connection->createPacketCollector(
			new PacketIDFilter(rosterPacket->getPacketID()));
	connection->sendPacket(rosterPacket);
	IQ * response = (IQ*) collector->nextResult(SmackConfiguration::getPacketReplyTimeout());
	collector->cancel();
	if (response == NULL) {
		//throw new XMPPException("No response from the server.");
	}
	// If the server replied with an error, throw an exception.
	else if (response->getType() == IQ::IQ_TYPE_ERROR) {
		//throw new XMPPException(response.getError());
	}

	// Create a presence subscription packet and send.
	Presence * presencePacket = new Presence(Presence::TYPE_SUBSCRIBE);
	presencePacket->setTo(user);
	connection->sendPacket(presencePacket);
}


void Roster::removeEntry(RosterEntry * entry) {
	// Only remove the entry if it's in the entry list.
	// The actual removal logic takes place in RosterPacketListenerprocess>>Packet(Packet)
	if ( entries.end() == std::find(entries.begin(), entries.end(), entry) ) {
		return;
	}
	RosterPacket * packet = new RosterPacket();
	packet->setType(IQ::IQ_TYPE_SET);
	RosterPacket::Item * item = RosterEntry::toRosterItem(entry);
	// Set the item type as REMOVE so that the server will delete the entry
	item->setItemType(RosterPacket::REMOVE);
	packet->addRosterItem(item);
	PacketCollector * collector = connection->createPacketCollector(
			new PacketIDFilter(packet->getPacketID()));
	connection->sendPacket(packet);
	IQ * response = (IQ*) collector->nextResult(SmackConfiguration::getPacketReplyTimeout());
	collector->cancel();
	if (response == NULL) {
	}
    // If the server replied with an error, throw an exception.
    else if (response->getType() == IQ::IQ_TYPE_ERROR) {
    }
}


std::list<RosterEntry*> Roster::getEntries() {
	std::list<RosterEntry*> allEntries;
	for (std::map<std::string, RosterGroup*>::iterator it = groups.begin();
			it != groups.end(); it++) {
		allEntries.merge((it->second)->getEntries());
	}
	allEntries.merge(unfiledEntries);
	return allEntries;
}


RosterEntry * Roster::getEntry(std::string & user) {
	if (user.empty()) {
		return NULL;
	}
	std::string userLowerCase = user;
	std::transform(userLowerCase.begin(), userLowerCase.end(), userLowerCase.begin(), ::tolower);
	for (std::vector<RosterEntry*>::iterator it = entries.begin(); 
			it != entries.end(); it++) {
		if ((*it)->getUser() == userLowerCase) {
			return *it;
		}
	}
	return NULL;
}


std::list<RosterGroup*> Roster::getGroups(){
	std::list<RosterGroup*> l;
	for ( std::map<std::string, RosterGroup*>::iterator it = groups.begin(); it != groups.end(); it++) {
		l.push_back(it->second);
	}
	return l;
}


Presence * Roster::getPresence(std::string & user) {
	std::string key = getPresenceMapKey(StringUtils::parseBareAddress(user));
	std::map<std::string, Presence*> userPresences = presenceMap[key];
	if (userPresences.empty()) {
		Presence * presence = new Presence(Presence::TYPE_UNAVAILABLE);
		presence->setFrom(user);
		return presence;
	} else {
		// Find the resource with the highest priority
		// Might be changed to use the resource with the highest availability instead.
		Presence * presence = NULL;

		for ( std::map<std::string, Presence*>::iterator it = userPresences.begin(); 
				it != userPresences.end(); it++) {
			Presence * p = it->second;
			// Chose presence with highest priority first.
			if ( NULL == presence || p->getPriority() > presence->getPriority()) {
				presence = p;
			} // If equal priority, choose "most available" by the mode value.
			else if ( p->getPriority() == presence->getPriority()) {
				if (p->getMode() > presence->getMode()) {
					presence = p;
				}
			}
		}

		if (presence == NULL) {
			presence = new Presence(Presence::TYPE_UNAVAILABLE);
			presence->setFrom(user);
			return presence;
		}
		else {
			return presence;
		}
	}
}


Presence * Roster::getPresenceResource(std::string & userWithResource) {
	std::string key = getPresenceMapKey(userWithResource);
	std::string resource = StringUtils::parseResource(userWithResource);
	std::map<std::string, Presence*> userPresences = presenceMap[key];
	if (userPresences.empty()) {
		Presence * presence = new Presence(Presence::TYPE_UNAVAILABLE);
		presence->setFrom(userWithResource);
		return presence;
	} else {
		Presence * presence = userPresences[resource];
		if (presence == NULL) {
			presence = new Presence(Presence::TYPE_UNAVAILABLE);
			presence->setFrom(userWithResource);
			return presence;
		}
		else {
			return presence;
		}
	}
}


std::string Roster::getPresenceMapKey(std::string user) {
	if (user.empty()) {
		return "";
	}
	std::string key = user;
	if (!contains(user)) {
		key = StringUtils::parseBareAddress(user);
	}
	std::transform(key.begin(), key.end(), key.begin(), ::tolower);
	return key;
}


void Roster::setOfflinePresences() {
	Presence * packetUnavailable;
	for (std::map<std::string, std::map<std::string, Presence*> >::iterator it = presenceMap.begin(); 
				it != presenceMap.end(); it++) {
		std::string user = it->first;
		std::map<std::string, Presence*> resources = it->second;
		if (!resources.empty()) {
			for (std::map<std::string, Presence*>::iterator it = resources.begin(); 
				it != resources.end(); it++) {
				packetUnavailable = new Presence(Presence::TYPE_UNAVAILABLE);
				std::string resource = it->first;
				user += "/" + resource;
				packetUnavailable->setFrom(user);
				presencePacketListener->processPacket(packetUnavailable);
			}
		}
	}
}


void Roster::fireRosterChangedEvent(
		std::vector<std::string> & addedEntries, 
		std::vector<std::string> & updatedEntries,
		std::vector<std::string> & deletedEntries ) 
{
	for (std::set<RosterListener*>::iterator it = rosterListeners.begin(); it != rosterListeners.end(); it++) {
		if (!addedEntries.empty()) {
			(*it)->entriesAdded(addedEntries);
		}
		if (!updatedEntries.empty()) {
			(*it)->entriesUpdated(updatedEntries);
		}
		if (!deletedEntries.empty()) {
			(*it)->entriesDeleted(deletedEntries);
		}
    }
}


void Roster::fireRosterPresenceEvent(Presence * presence) {
	std::for_each(rosterListeners.begin(), rosterListeners.end(), std::bind2nd(std::mem_fun(&RosterListener::presenceChanged), presence));
}


void Roster::PresencePacketListener::processPacket(Packet * packet) {
}


void Roster::RosterPacketListener::processPacket(Packet * packet) {
}
